﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;

namespace com.susi.util {
    public static class IOUtil {
        /// <summary>
        ///     用于读取文件的文件流
        ///     以只读方式打开，不锁定
        /// </summary>
        public static FileStream ReadStream(string fullfilename)
        {
            return !File.Exists(fullfilename) ? null : new FileStream(fullfilename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
        }

        /// <summary>
        ///     用于创建文件的文件流,会自动创建不存在的文件夹
        /// </summary>
        public static FileStream CreatStream(string fullfilename)
        {
            var dir = Path.GetDirectoryName(fullfilename)?.Replace('\\', '/');
            if (dir == null) return null;
            if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
            return new FileStream(fullfilename, FileMode.Create, FileAccess.Write, FileShare.Read);
        }

        /// <summary>
        ///     批量选取文件打开
        /// </summary>
        /// <returns>FileStream的Creater</returns>
        public static IEnumerable<Func<FileStream>> ReadFolderStream(
            string folder,
            string searchpattern          = null,
            string regularpatternPath     = null,
            string regularpatternFilename = null,
            bool   tolower                = false
        )
        {
            var regex_path     = regularpatternPath == null ? null : new Regex(regularpatternPath, RegexOptions.Compiled);
            var regex_filename = regularpatternFilename == null ? null : new Regex(regularpatternFilename, RegexOptions.Compiled);


            var files = Directory.EnumerateFiles(folder, searchpattern ?? "*", SearchOption.AllDirectories)
                                 .Where(v =>
                                        {
                                            v = tolower ? v.Replace('\\', '/').ToLowerInvariant() : v.Replace('\\', '/');
                                            var isPassPathRegx = regex_path == null || v.IsMatch(regex_path);
                                            var filename       = v.GetPathFileName();
                                            var isPassFilename = regex_filename == null || filename.IsMatch(regex_filename);
                                            return isPassPathRegx && isPassFilename;
                                        });
            return files.Select(v => new Func<FileStream>(() => ReadStream(v)));
        }

        /// <summary>
        ///     清空目录，有任何异常返回false，但仍会清空所有其他正常的文件和目录，不抛异常
        /// </summary>
        public static bool ClearDir(string folder)
        {
            var dir  = new DirectoryInfo(folder);
            var isEr = false;
            foreach (var file in dir.EnumerateFiles("*", SearchOption.AllDirectories))
                try {
                    file.Delete();
                }
                catch {
                    Debug.LogWarning($"[warn]{file}删除失败");
                    isEr = true;
                }

            foreach (var indir in dir.EnumerateDirectories())
                try {
                    indir.Delete();
                }
                catch {
                    Debug.LogWarning($"[warn]{indir}删除失败");
                    isEr = true;
                }

            return isEr;
        }

        /// <summary>
        ///     从0写入bytes
        /// </summary>
        public static void Write(this FileStream fs, byte[] bytes)
        {
            fs.Write(bytes, 0, bytes.Length);
        }

        /// <summary>
        ///     无提示,只有cwlog
        /// </summary>
        public static bool SaveFileCollection(IDictionary<string, byte[]> fullfilename_filebytes, string defaultFolder, bool warpFolder = false, bool creatDefault = true)
        {
            if (creatDefault && !Directory.Exists(defaultFolder)) Directory.CreateDirectory(defaultFolder);

            var selectFolder = defaultFolder;

            if (warpFolder) ClearDir(selectFolder);

            var isEr = false;
            foreach (var fulname_bytes in fullfilename_filebytes)
                try {
                    using (var fs = CreatStream(fulname_bytes.Key)) {
                        fs.Write(fulname_bytes.Value);
                    }
                }
                catch (Exception ex) {
#if USING_LITJSON
                    Console.WriteLine($"[error]{fulname_bytes.Key}写入失败。\n异常消息{ex}\n文件前10byte内容如下：\n{fulname_bytes.Value.Take(10).ToArray().SerializeToJson()}");
#else
                    Console.WriteLine($"[error]{fulname_bytes.Key}写入失败。\n异常消息{ex}\n文件前10byte内容如下：\n{fulname_bytes.Value.Take(10).ToArray().SerializeToPrettyXML()}");
#endif
                    isEr = true;
                }

            return isEr;
        }
#if UNITY_EDITOR

        /// <summary>
        ///     在UnityEditor环境下打开保存文件对话框保存文件
        ///     <para>无异常</para>
        /// </summary>
        public static void SaveFileByPlane(IEnumerable<byte> data, string ext = null, string defaultName = null, string defaultFolder = null, bool creatDefaultFolder = false)
        {
            SaveFileByPlane(data.ToArray(), ext, defaultName, defaultFolder, creatDefaultFolder);
        }

        /// <summary>
        ///     在UnityEditor环境下打开保存文件对话框保存文件
        ///     ext不用带.号
        ///     <para>无异常</para>
        /// </summary>
        public static bool SaveFileByPlane(byte[] data, string ext = null, string defaultName = null, string defaultFolder = null, bool creatDefaultFolder = false)
        {
            if (creatDefaultFolder && defaultFolder != null && !Directory.Exists(defaultFolder)) Directory.CreateDirectory(defaultFolder);

            var selectfullfilename = EditorUtility.SaveFilePanel(
                "Save File",
                defaultFolder,
                defaultName,
                ext
            );
            if (selectfullfilename == null) {
                Debug.Log("[notice]没有保存文件");
                return false;
            }

            try {
                using (var fs = CreatStream(selectfullfilename)) {
                    fs.Write(data, 0, data.Length);
                }

                return true;
            }
            catch (Exception ex) {
                Debug.LogError($"[error]保存文件失败!\n{ex.Message}\n{ex.StackTrace}");
                return false;
            }
        }

        /// <summary>
        ///     在UnityEditor环境下打开保存文件对话框保存文件
        ///     <para>无异常</para>
        /// </summary>
        public static bool SaveFileByPlane(string strsource, string ext = null, string defaultName = null, string defaultFolder = null, bool creatDefaultFolder = false)
        {
            var bytes = Encoding.UTF8.GetBytes(strsource);
            return SaveFileByPlane(bytes, ext, defaultName, defaultFolder, creatDefaultFolder);
        }


        /// <summary>
        ///     自动创建文件 ;任何异常都返回false
        /// </summary>
        public static bool SaveFileCollectionByPlane(IDictionary<string, byte[]> fullfilename_filebytes, string defaultFolder, bool warpFolder = false, bool creatDefault = true)
        {
            if (creatDefault && !Directory.Exists(defaultFolder)) Directory.CreateDirectory(defaultFolder);
            var selectFolder = EditorUtility.SaveFolderPanel("选择保存文件夹", defaultFolder, "");
            if (selectFolder == null) {
                Debug.Log("[debug]没有选择文件夹,本次保存取消");
                return false;
            }

            if (warpFolder) {
                if (!EditorUtility.DisplayDialog("清空确认", $"即将清空文件夹{selectFolder}", "ok", "cancel")) {
                    Debug.Log("[debug]取消了清空文件夹，本次保存取消");
                    return false;
                }

                ClearDir(selectFolder);
            }

            var isEr = false;
            foreach (var fulname_bytes in fullfilename_filebytes)
                try {
                    using (var fs = CreatStream(fulname_bytes.Key)) {
                        fs.Write(fulname_bytes.Value);
                    }
                }
                catch (Exception ex) {
#if USING_LITJSON
                    Debug.LogError($"[error]{fulname_bytes.Key}写入失败。\n异常消息{ex}\n文件前10byte内容如下：\n{fulname_bytes.Value.Take(10).ToArray().SerializeToJson()}");
#endif
                    isEr = true;
                }

            return isEr;
        }

#endif
    }
}